import java.awt.AWTException;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Insets;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Map;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

import paskal.abstractplayer.AbstractPlayer;
import paskal.abstractplayer.PlaylistListener;
import paskal.abstractplayer.VkTrack;
import paskal.audio.player.PlayerEvent;
import paskal.audio.player.PlayerListener;
import paskal.ui.Style;
import paskal.ui.components.Button;
import paskal.ui.components.Component;

public class Main extends JDialog implements Component, PlayerListener,
		PlaylistListener, ComponentListener, WindowListener {
	private int borderRadius = 0;
	private int oldBorderRadius = 0;
	private int LEFT = 0;
	private int TOP = 1;
	private int RIGHT = 2;
	private int BOTTOM = 3;
	private int nearTo = RIGHT;
	private boolean isMoving = true;
	private Dimension showButtonVSize;
	private BufferedImage showButtonIcon;
	private Dimension showButtonHSize;
	private static final long serialVersionUID = 1L;
	private AbstractPlayer player = null;
	private Toolkit toolkit = null;
	private Point position;
	private PlaylistPanel playlistPanel;
	private PlayerPanel playerPanel;
	private static boolean isPlaylistVisible = false;
	private static boolean isPlayerVisible = true;
	private static Main instance;

	private MouseAdapter draggingListener = new MouseAdapter() {
		int x;
		int y;

		public void mousePressed(MouseEvent e) {
			setCursor(new Cursor(Cursor.MOVE_CURSOR));
			x = e.getXOnScreen();
			y = e.getYOnScreen();
		};

		public void mouseDragged(MouseEvent e) {
			setLocation(position.x + e.getXOnScreen() - x,
					position.y + e.getYOnScreen() - y);
			position = getLocation();
			x = e.getXOnScreen();
			y = e.getYOnScreen();
		};

		public void mouseReleased(MouseEvent e) {
			setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
		};
	};

	private Button showButton = null;
	private SystemTray systemTray = SystemTray.getSystemTray();
	private TrayIcon trayIcon;

	public Main() {
		instance = this;
		player = new AbstractPlayer(this);
		player.addPlaylistListener(this);
		setModal(false);
		setTitle("RelaxPlayer");
		setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		addMouseListener(draggingListener);
		addMouseMotionListener(draggingListener);
		addComponentListener(this);
		addWindowListener(this);
		KeyboardFocusManager.getCurrentKeyboardFocusManager()
				.addKeyEventDispatcher(new KeyEventDispatcher() {
					private boolean isOpened = false;

					@Override
					public boolean dispatchKeyEvent(KeyEvent e) {
						if (!isOpened) {
							if (e.isControlDown() && e.isShiftDown()
									&& (e.getKeyCode() == KeyEvent.VK_G)) {
								SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {
									@Override
									protected Void doInBackground()
											throws Exception {
										File source = player.getFile();
										if (source != null) {
											isOpened = true;
											JFileChooser chooser = new JFileChooser(
													System.getProperty("user.home"));
											VkTrack t = player.getPlaylist()
													.get();
											chooser.setSelectedFile(new File(t
													.toString()
													+ "."
													+ t.getExtention()));
											if (chooser
													.showSaveDialog(Main.this) == JFileChooser.APPROVE_OPTION) {
												File temp = chooser
														.getSelectedFile();
												if (temp != null) {
													try {
														Files.copy(
																Paths.get(source
																		.toURI()),
																Paths.get(temp
																		.toURI()),
																StandardCopyOption.REPLACE_EXISTING);
													} catch (IOException e1) {
														e1.printStackTrace();
													}
												}
											}
										}
										isOpened = false;
										Main.getInstance().showItself();
										return null;
									}
								};
								sw.execute();
							}
						}
						return false;
					}
				});
		toolkit = Toolkit.getDefaultToolkit();
		setUndecorated(true);
		setBackground(new Color(0, 0, 0, 0));
		setAlwaysOnTop(true);
		setLayout(null);
		stylize("frame");
		position = new Point((toolkit.getScreenSize().width - getWidth()) / 2,
				(int) (toolkit.getScreenSize().height / 2));
		setLocation(position);
		initGUI();

		// add tray icon
		BufferedImage temp = Style.getInstance().getImage(
				"buttons_original2.png", new Rectangle(0, 35, 35, 35));
		trayIcon = new TrayIcon(temp);
		trayIcon.setImage(temp.getScaledInstance(trayIcon.getSize().width,
				trayIcon.getSize().height, Image.SCALE_SMOOTH));
		trayIcon.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				showItself();
			}
		});
		try {
			systemTray.add(trayIcon);
		} catch (AWTException e1) {
			e1.printStackTrace();
		}
		setVisible(true);
	}

	public static boolean isPlaylistVisible() {
		return isPlaylistVisible;
	}

	private void initGUI() {
		showButton = new Button();
		showButton.stylize("showButton");
		showButtonIcon = Style.iconToBufferedImage(showButton.getIcon(),
				getGraphicsConfiguration());
		showButtonVSize = showButton.getSize();
		showButtonHSize = new Dimension(showButtonVSize.height,
				showButtonVSize.width);
		showButton.setVisible(false);
		showButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				showItself();
			}
		});
		add(showButton);

		playerPanel = new PlayerPanel(player);
		playerPanel.addMouseListener(draggingListener);
		playerPanel.addMouseMotionListener(draggingListener);
		add(playerPanel);

		playlistPanel = new PlaylistPanel(player);
		playlistPanel.addMouseListener(draggingListener);
		playlistPanel.addMouseMotionListener(draggingListener);
		playlistPanel.setVisible(false);
		add(playlistPanel);
	}

	public void showPlaylist() {
		if (!isPlaylistVisible) {
			isPlaylistVisible = true;
			setSize(getWidth(),
					playerPanel.getHeight() + playlistPanel.getHeight());
			playlistPanel.setVisible(true);
		}
	}

	public void hidePlaylist() {
		if (isPlaylistVisible) {
			isPlaylistVisible = false;
			playlistPanel.setVisible(false);
			setSize(getWidth(), playerPanel.getHeight());
		}
	}

	@Override
	public void setProperty(String key, Object value) {
		switch (key) {
		case "size": {
			Point p = (Point) value;
			setSize(p.x, p.y);
			break;
		}
		case "border": {
			@SuppressWarnings("unchecked")
			Map<String, Object> border = (Map<String, Object>) value;
			if (border.get("radius") != null) {
				borderRadius = (int) border.get("radius");
				oldBorderRadius = borderRadius;
			}
			break;
		}
		default:
			break;
		}
	}

	@Override
	public void stylize(String selector) {
		paskal.ui.Style.getInstance().styleThisShit(selector, this);
	}

	public static void main(String[] args) {
		paskal.ui.Style.getInstance().loadFromJson("site_style.json");
		// paskal.ui.Style.getInstance().loadFromJson("style.json");
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				new Main();
			}
		});
	}

	@Override
	public void onOpened(Object paramObject, Map<String, Object> properties) {
		// System.out.println(properties);
		player.setAudioInfo(properties);
		playerPanel.updateTrackLabel(player.getTitle());
		playlistPanel.updateActiveTrackIndex();
		playerPanel.updateTimeLabel(0);

		if (player.getDuration() != -1) {
			playerPanel.updateDurationLabel(player.getDuration());
		} else {
			playerPanel.updateDurationLabel(0);
		}

		playlistPanel.updateShowLirycsButtonVisible();
		playlistPanel.hideLyrics();
	}

	@Override
	public void onRunning(double percentagePosition,
			Map<String, Object> properties) {
		if (properties != null) {
			if (properties.get("mp3.shoutcast.metadata.StreamTitle") != null) {
				playerPanel.updateTrackLabel(properties.get(
						"mp3.shoutcast.metadata.StreamTitle").toString());
			} else {
				playerPanel.updateTrackLabel(player.getTitle());
			}
		}
		if (percentagePosition > 0) {
			playerPanel.updateTimeLabel((int) (percentagePosition * player
					.getDuration()));
			playerPanel
					.updateSliderValue((int) (percentagePosition * playerPanel
							.getSliderMaximum()));
		}
	}

	@Override
	public void onLoading(double percentage) {
		playerPanel.setLoadedPercentage(percentage);
	}

	@Override
	public void onStateUpdated(PlayerEvent event) {
		// System.out.println(event);
		if (event.getCode() == PlayerEvent.EOM) {
			playerPanel.updateSliderValue(0);
			player.next();
			player.play();
		}
		if (event.getCode() == PlayerEvent.PLAYING) {
			player.setVolume(playerPanel.getPVolumeValue());
		}

		if (event.getCode() == PlayerEvent.CAHSING) {
			playerPanel.showCashing(true);
		}

		if (event.getCode() == PlayerEvent.CAHSED) {
			playerPanel.showCashing(false);
		}

		if (player.isRunning()) {
			playerPanel.setPauseVisible();
		} else {
			playerPanel.setPlayVisible();
		}
	}

	public AbstractPlayer getPlayer() {
		return player;
	}

	public void setPlayer(AbstractPlayer player) {
		this.player = player;
	}

	@Override
	public void onPlayListUpdated() {
		player.play(player.getPlaylist().getCursor());
	}

	public static Main getInstance() {
		return instance;
	}

	public void showItself() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				if (!isPlayerVisible) {
					borderRadius = oldBorderRadius;
					isPlayerVisible = true;
					showButton.setVisible(false);
					playerPanel.setVisible(true);
					int tHeight = playerPanel.getHeight();
					if (isPlaylistVisible) {
						playlistPanel.setVisible(true);
						tHeight += playlistPanel.getHeight();
					}
					setSize(playerPanel.getWidth(), tHeight);
					Insets effectiveZone = toolkit
							.getScreenInsets(getGraphicsConfiguration());
					int start = 0;
					int end = 0;
					int step = 10;
					if (nearTo == LEFT) {
						start = effectiveZone.left - getWidth();
						end = effectiveZone.left;
						step = 10;
					} else if (nearTo == TOP) {
						start = effectiveZone.top - getHeight();
						end = effectiveZone.top;
						step = 10;
					} else if (nearTo == RIGHT) {
						start = toolkit.getScreenSize().width
								- effectiveZone.right;
						end = start - getWidth();
						step = -10;
					} else if (nearTo == BOTTOM) {
						start = toolkit.getScreenSize().height
								- effectiveZone.bottom;
						end = start - getHeight();
						step = -10;
					}
					for (int i = start; Math.abs(i - end) - 10 >= 0; i += step) {
						if ((nearTo == BOTTOM) || (nearTo == TOP)) {
							setLocation(position.x, i);
						} else {
							setLocation(i, position.y);
						}
						try {
							Thread.sleep(15);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					if ((nearTo == BOTTOM) || (nearTo == TOP)) {
						setLocation(position.x, end);
					} else {
						setLocation(end, position.y);
					}
					isMoving = true;
					position = getLocation();
				}
			}
		}).start();
	}

	public void hideItself() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				if (isPlayerVisible) {
					isMoving = false;
					isPlayerVisible = false;
					borderRadius = 0;
					Insets effectiveZone = toolkit
							.getScreenInsets(getGraphicsConfiguration());
					int start = 0;
					int end = 0;
					int step = 10;
					if (nearTo == LEFT) {
						start = position.x;
						end = effectiveZone.left - getWidth();
						step = -10;
					} else if (nearTo == TOP) {
						start = position.y;
						end = effectiveZone.top - getHeight();
						step = -10;
					} else if (nearTo == RIGHT) {
						end = toolkit.getScreenSize().width
								- effectiveZone.right;
						start = position.x;
						step = 10;
					} else if (nearTo == BOTTOM) {
						end = toolkit.getScreenSize().height
								- effectiveZone.bottom;
						start = position.y;
						step = 10;
					}
					for (int i = start; Math.abs(i - end) - 10 >= 0; i += step) {
						if ((nearTo == BOTTOM) || (nearTo == TOP)) {
							setLocation(position.x, i);
						} else {
							setLocation(i, position.y);
						}
						try {
							Thread.sleep(15);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					playerPanel.setVisible(false);
					playlistPanel.setVisible(false);
					showButton.setVisible(true);
					Icon icon = new ImageIcon(showButtonIcon);
					if (nearTo == LEFT) {
						showButton.setSize(showButtonVSize);
						setLocation(effectiveZone.left, position.y
								+ (getHeight() - showButton.getHeight()) / 2);
						icon = Style.rotateImageIcon(showButtonIcon, 2);
					} else if (nearTo == TOP) {
						showButton.setSize(showButtonHSize);
						setLocation(
								position.x
										+ (getWidth() - showButton.getWidth())
										/ 2, effectiveZone.top);
						icon = Style.rotateImageIcon(showButtonIcon, -1);
					} else if (nearTo == RIGHT) {
						showButton.setSize(showButtonVSize);
						setLocation(end - showButton.getWidth(), position.y
								+ (getHeight() - showButton.getHeight()) / 2);
					} else if (nearTo == BOTTOM) {
						showButton.setSize(showButtonHSize);
						setLocation(
								position.x
										+ (getWidth() - showButton.getWidth())
										/ 2, end - showButton.getHeight());
						icon = Style.rotateImageIcon(showButtonIcon, 1);
					}
					showButton.setIcon(icon);
					showButton.setRolloverIcon(icon);
					showButton.setPressedIcon(icon);
					setSize(showButton.getSize());
				}
			}
		}).start();
	}

	@Override
	public void componentResized(ComponentEvent e) {
		setShape(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(),
				borderRadius, borderRadius));
	}

	@Override
	public void componentMoved(ComponentEvent e) {
		if (isMoving) {
			int screenWidth = toolkit.getScreenSize().width;
			int screenHeight = toolkit.getScreenSize().height;
			int x = position.x + getWidth() / 2;
			int y = position.y + getHeight() / 2;
			LEFT = x;
			RIGHT = screenWidth - x;
			TOP = y;
			BOTTOM = screenHeight - y;
			nearTo = Math.min(LEFT, RIGHT);
			nearTo = Math.min(nearTo, TOP);
			nearTo = Math.min(nearTo, BOTTOM);
		}
	}

	@Override
	public void windowClosed(WindowEvent e) {
		player.stop();
		player.close();
		System.exit(0);
	}

	@Override
	public void windowDeactivated(WindowEvent e) {
		hideItself();
	}

	@Override
	public void componentShown(ComponentEvent e) {
	}

	@Override
	public void componentHidden(ComponentEvent e) {
	}

	@Override
	public void windowOpened(WindowEvent e) {
	}

	@Override
	public void windowClosing(WindowEvent e) {
	}

	@Override
	public void windowIconified(WindowEvent e) {
	}

	@Override
	public void windowDeiconified(WindowEvent e) {
	}

	@Override
	public void windowActivated(WindowEvent e) {
	}
}